
package artistic;

import java.util.HashSet;

/**
 *
 * @author Mikael Andersson
 * @author Rurik Högfeldt
 */
public class TiledCanvas {

    public class UpdateEntry
    {
        private int index;
        public UpdateEntry(int index)
        {
            this.index = index;
        }
        public int getIndex(){ return index; }

        @Override
        public int hashCode()
        {
            //index*31
            return (index<<5)-1;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final UpdateEntry other = (UpdateEntry) obj;
            if (this.index != other.index) {
                return false;
            }
            return true;
        }
    }

    private HashSet<UpdateEntry> updateSet = new HashSet<UpdateEntry>();

    public TiledCanvas(int width, int height)
    {
        this(width, height, CanvasTile.FillType.TRANSPARENT);
    }

    public TiledCanvas(int width, int height, CanvasTile.FillType fillType)
    {
        this.width = width;
        this.height = height;

        int gridWidth = getGridWidth();
        int gridHeight = getGridHeight();
        int count = gridWidth*gridHeight;
        
        tileArray = new CanvasTile[count];
        for(int i=0; i<tileArray.length; ++i)
        {
            tileArray[i] = new CanvasTile(fillType);
        }
    }

    private CanvasTile[] tileArray;

    public CanvasTile[] getTiles()
    {
        return tileArray;
    }

    public CanvasTile getTile(int gridX, int gridY)
    {
        return tileArray[this.getGridIndex(gridX<<6, gridY<<6)];
    }

    public void setSize(int width, int height)
    {
        int oldWidth = this.width;
        int oldHeight = this.height;
        int oldGridWidth = getGridWidth();
        int oldGridHeight = getGridHeight();
        CanvasTile[] oldArray = tileArray;
        
        this.width = width;
        this.height = height;

        int gridWidth = getGridWidth();
        int gridHeight = getGridHeight();
        int count = gridWidth*gridHeight;
        tileArray = new CanvasTile[count];
        for(int i=0; i<tileArray.length; ++i)
        {
            tileArray[i] = new CanvasTile();
        }
        
        /**
         * Copy old tiles to new canvas
         */
        if(oldArray != null)
        {
            int gw = Math.min(oldGridWidth, gridWidth);
            int gh = Math.min(oldGridHeight, gridHeight);
            for(int gy = 0; gy < gh; ++gy)
            {
                int srcIndex = oldGridWidth*gy;
                int destIndex = gridWidth*gy;
                for(int gx = 0; gx < gw; ++gx)
                {
                    tileArray[destIndex] = oldArray[srcIndex];
                    ++destIndex;
                    ++srcIndex;
                }
            }
        }

        /*for(int y=0; y<height; ++y)
        {
            for(int x=0; x<width; ++x)
            {
                setPixel(x, y, 0xfff0000);
            }
        }*/
    }
    
    public int getGridIndex(int x, int y)
    {
        if(x<0 || y<0)
            throw new RuntimeException("Pixel out of bounds.");
        int gridX = x>>6;
        int gridY = y>>6;
        if(gridX>=getGridWidth())
            throw new RuntimeException("Pixel out of bounds.");
        if(gridY>=getGridHeight())
            throw new RuntimeException("Pixel out of bounds.");
        int index = gridY*getGridWidth()+gridX;
        return index;
    }

    public void drawPixel(int x, int y, int color)
    {
        int index;
        try {
            index = getGridIndex(x,y);
        }
        catch(RuntimeException e) {
            return;
        }
        tileArray[index].drawPixel(x&63, y&63, color);

        updateSet.add(new UpdateEntry(index));
    }
    
    public void setPixel(int x, int y, int color)
    {
        int index;
        try {
            index = getGridIndex(x,y);
        }
        catch(RuntimeException e) {
            return;
        }
        tileArray[index].setPixel(x&63, y&63, color);

        updateSet.add(new UpdateEntry(index));
    }

    public int getPixel(int x, int y)
    {
        int index;
        index = getGridIndex(x,y);
        return  tileArray[index].getPixel(x&63, y&63);
    }



    public void copyFrom(TiledCanvas canvas)
    {
        blendWith(canvas, new CopyBlender());
    }

    public void blendWith(TiledCanvas canvas, CanvasBlender blender)
    {
        int gw = Math.min(this.getGridWidth(), canvas.getGridWidth());
        int gh = Math.min(this.getGridHeight(), canvas.getGridHeight());
        for(int gy = 0; gy < gh; ++gy)
        {
            for(int gx = 0; gx < gw; ++gx)
            {
                blender.apply(getTile(gx, gy), canvas.getTile(gx, gy));
            }
        }
    }

    public void blendWith(TiledCanvas canvas)
    {
        blendWith(canvas, new NormalBlender());
    }

    private int width;
    private int height;

    /**
     * Returns width of canvas
     *
     * @return Canvas width.
     */
    public int getWidth() { return width; }

    /**
     * Returns height of canvas.
     *
     * @return Canvas height.
     */
    public int getHeight() { return height; }

    /**
     * Returns the horizontal amount of
     * 64x64 tiles in canvas.
     *
     * @return Grid width, in 64x64 tiles.
     */
    public int getGridWidth()
    {
        return (width+63)>>6;
    }

    /**
     * Returns the vertical amount of
     * 64x64 tiles in canvas.
     *
     * @return Grid height, in 64x64 tiles.
     */
    public int getGridHeight()
    {
        return (height+63)>>6;
    }
}
